#!/usr/bin/perl

# This script will parse through assembly language code and produce a *.hex
# dump file and a COE file. 
#
# Usage: compiler.pl <asm_file> (no extension)
#
# Author: Cody Cziesler

use Switch;

# Check if called correctly
(@ARGV == 2) or die "usage: compiler.pl <test_name> <dir>\n";

# Save arguements as variables for file names
$dir     = $ARGV[1];

`mkdir $ARGV[0]-work/`;

$asmfile = $dir . "/" . $ARGV[0] . ".asm";
$hexfile = $ARGV[0] . "-work/" . $ARGV[0] . ".hex";
$coefile = $ARGV[0] . "-work/" . $ARGV[0] . ".coe";

open (ASM_IN, $asmfile)     or die "Can't open $asmfile\nDid you try without the extension?\n";
open (HEX_OUT, ">$hexfile") or die "Can't open $hexfile\n";
open (COE_OUT, ">$coefile") or die "Can't open $coefile\n";

printf COE_OUT "MEMORY_INITIALIZATION_RADIX=2;\n";
printf COE_OUT "MEMORY_INITIALIZATION_VECTOR=\n";

# Local variables
my @adata = <ASM_IN>;
my @idata;
my @mdata;
my $count = 0;

# Parse asm file
foreach $i (@adata) {
  my @hdata;
  @inst = split(/ /, $i);
  chomp(@inst);

  for($j = 1; $j <= 3; $j++) {
    switch (@inst[$j]) {
      case '$0' { @inst[$j] = 0; }
      case '$A' { @inst[$j] = 1; }
      case '$B' { @inst[$j] = 2; }
      case '$C' { @inst[$j] = 3; }
      case '$D' { @inst[$j] = 4; }
      case '$E' { @inst[$j] = 5; }
      case '$F' { @inst[$j] = 6; }
      case '$G' { @inst[$j] = 7; }
      else { }
    }
  }

  switch (@inst[0]) {
    #                           OPCODE              SOURCE 1              DESTINATION           SOURCE 2              FUNCTION
    case "NOOP"   { @hdata[1] = 0b0000; @hdata[2] = 0b000;    @hdata[3] = 0b000;    @hdata[4] = 0b000;    @hdata[5] = 0b000;  }
    case "CPY"    { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b001;  }
    case "ADD"    { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b010;  }
    case "SUB"    { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b011;  }
    case "MUL"    { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b100;  }
    case "AND"    { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b101;  }
    case "OR"     { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b110;  }
    case "NOT"    { @hdata[1] = 0b0000; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = 0b000;    @hdata[5] = 0b111;  }
    case "XOR"    { @hdata[1] = 0b0001; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b000;  }
    case "NOR"    { @hdata[1] = 0b0001; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b001;  }
    case "XNOR"   { @hdata[1] = 0b0001; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b010;  }
    case "LS"     { @hdata[1] = 0b0001; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b011;  }
    case "RS"     { @hdata[1] = 0b0001; @hdata[2] = @inst[2]; @hdata[3] = @inst[1]; @hdata[4] = @inst[3]; @hdata[5] = 0b100;  }
    case "INC"    { @hdata[1] = 0b0001; @hdata[2] = @inst[1]; @hdata[3] = @inst[1]; @hdata[4] = 0b000;    @hdata[5] = 0b101;  }
    case "DEC"    { @hdata[1] = 0b0001; @hdata[2] = @inst[1]; @hdata[3] = @inst[1]; @hdata[4] = 0b000;    @hdata[5] = 0b110;  }
    case "ZERO"   { @hdata[1] = 0b0001; @hdata[2] = 0b000;    @hdata[3] = 0b000;    @hdata[4] = 0b000;    @hdata[5] = 0b111;  }
    case "BEQ"    { @hdata[1] = 0b0011; @hdata[2] = @inst[1]; @hdata[3] = @inst[2]; @hdata[4] = @inst[3]; }
    case "BNE"    { @hdata[1] = 0b0100; @hdata[2] = @inst[1]; @hdata[3] = @inst[2]; @hdata[4] = @inst[3]; }
    case "LD"     { @hdata[1] = 0b0101; @hdata[2] = 0b000;    @hdata[3] = @inst[1]; @hdata[4] = @inst[2]; }
    case "STR"    { @hdata[1] = 0b0110; @hdata[2] = 0b000;    @hdata[3] = @inst[1]; @hdata[4] = @inst[2]; }
    case "JMP"    { @hdata[1] = 0b1111; @hdata[2] = @inst[1]; }
    else {
      printf "compiler: ERROR - Unknown Opcode: %s\n", @inst[0];
    }
  }
  # {opcode,r1,r2,r3,00}
  printf HEX_OUT "// @inst\n";
  printf HEX_OUT "\@%02x ", $count;

  switch (@inst[0]) {
    case "NOOP" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "CPY" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "ADD" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "SUB" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "MUL" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "AND" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "OR" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "NOT" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "XOR" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "NOR" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "XNOR" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "LS" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "RS" {    
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "INC" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "DEC" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "ZERO" {  
      printf HEX_OUT "%04b%03b%03b%03b%03b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      } else {
        printf COE_OUT "%04b%03b%03b%03b%03b",@hdata[1],@hdata[2],@hdata[3],@hdata[4],@hdata[5];
      }
    }
    case "BEQ" {
      @hdata[4] = @hdata[4] & 0b111111;
      printf HEX_OUT "%04b%03b%03b%06b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      } else {
        printf COE_OUT "%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      }
    }
    case "BNE" {
      @hdata[4] = @hdata[4] & 0b111111;
      printf HEX_OUT "%04b%03b%03b%06b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      } else {
        printf COE_OUT "%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      }
    }
    case "LD" {
      printf HEX_OUT "%04b%03b%03b%06b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      } else {
        printf COE_OUT "%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      }
    }
    case "STR" {
      printf HEX_OUT "%04b%03b%03b%06b\n",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      } else {
        printf COE_OUT "%04b%03b%03b%06b",@hdata[1],@hdata[2],@hdata[3],@hdata[4];
      }
    }
    case "JMP" {
      @hdata[2] = @hdata[2] & 0b111111111111;
      printf HEX_OUT "%04b%012b\n",@hdata[1],@hdata[2];
      if ($count != 0) {
        printf COE_OUT ",\n%04b%012b",@hdata[1],@hdata[2];
      } else {
        printf COE_OUT "%04b%012b",@hdata[1],@hdata[2];
      }    
    }
  }
  $count++;
}

printf COE_OUT ";\n";

# Close files
close ASM_IN;
close HEX_OUT;
close COE_OUT;
